﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Xml;
using OpenLS.Core.Serialization;
using OpenLS.Drawing;
using OpenLS.Spreadsheet.Serialization;

namespace OpenLS.Spreadsheet
{
    /// <summary>
    /// Represents an arbitrary element anchored in a worksheet. The element is serialized using XAML in the spreadsheet document.
    /// </summary>
    public class XamlElement : IAnchored
    {
        private const string tag = "Xaml Embedded Object";
        private string _xamlText;
        private FrameworkElement _content;

        /// <summary>
        /// Initializes a new empty XamlElement
        /// </summary>
        private XamlElement()
        {
        }
        /// <summary>
        /// Initializes a XamlElement and sets the Content by deserializing the xamlText
        /// </summary>
        /// <param name="xamlText">The Xaml text to deserialize</param>
        public XamlElement(string xamlText)
        {
            Contract.Requires(xamlText != null);
            XamlText = xamlText;
        }
        /// <summary>
        /// Initializes a XamlElement and sets the Content
        /// </summary>
        /// <param name="xamlText">The content</param>
        public XamlElement(FrameworkElement content)
        {
            Contract.Requires(content != null);
            this._content = content;
            XamlText = XamlWriter.Save(content);
        }
        /// <summary>
        /// Get the type of frame to use for hosting in the worksheet
        /// </summary>
        public AnchorFrameType FrameType
        {
            get { return AnchorElement.GetFrameType(Content); }
        }
        /// <summary>
        /// Get the content of the current element
        /// </summary>
        public FrameworkElement Content
        {
            get
            {
                if (_content == null && XamlText != null)
                    try
                    {
                        _content = (FrameworkElement)XamlReader.Parse(XamlText);
                        if (_content.Parent != null)
                        {
                            FrameworkElement parent = (FrameworkElement)_content.Parent;
                            throw new NotSupportedException();
                        }
                    }
                    catch
                    {
                    }
                return _content;
            }
            set
            {
                _content = value;
            }
        }


        /// <summary>
        /// Get or set the Xaml text that represents the content
        /// </summary>
        public string XamlText
        {
            get { return _xamlText; }
            set
            {
                _xamlText = value;
                try
                {
                    if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
                        Content = (FrameworkElement) XamlReader.Parse((value));
                }
                catch
                {
                }
            }
        }

        #region IAnchored Members

        void IOfficeXmlSerializable.ReadXml(ReadContext context)
        {
            throw new NotSupportedException();
        }

        void IOfficeXmlSerializable.WriteXml(WriteContext context)
        {
            ToPicture().WriteXml(context);
        }

        #endregion

        internal static IAnchored FromPicture(Picture picture, IXamlElementResolver resolver)
        {
            if (resolver == null)
                resolver = new DefaultXamlElementResolver();
            return resolver.Resolve(picture);
        }

        internal Picture ToPicture()
        {
            var result = new Picture();
            result.PictureNonVisual.NonVisualDrawingProps.Title = tag;
            result.PictureNonVisual.NonVisualDrawingProps.Description = XamlText;

            int pixelWidth = Math.Max((int)Math.Ceiling(Content.ActualWidth), 1);
            int pixelHeight = Math.Max((int)Math.Ceiling(Content.ActualHeight), 1);
            var renderTargetBitmap = new RenderTargetBitmap(pixelWidth, pixelHeight, 96, 96, PixelFormats.Pbgra32);
            renderTargetBitmap.Render(Content);
            var pngBitmapEncoder = new PngBitmapEncoder();
            pngBitmapEncoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
            var stream = new MemoryStream();
            pngBitmapEncoder.Save(stream);
            stream.Position = 0;
            var bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = stream;
            bitmapImage.EndInit();
            result.BlipFillProperties = new BlipFillProperties(bitmapImage);
            return result;
        }
        Dictionary<WeakReference, WeakReference> _table = new Dictionary<WeakReference, WeakReference>();

        internal FrameworkElement GetContentFor(ContentControl contentControl)
        {
            foreach (var key in Enumerable.ToList(_table.Keys))
            {
                if (key.Target == contentControl)
                    return (FrameworkElement)_table[key].Target;
            }
            try
            {
                FrameworkElement r = (FrameworkElement)XamlReader.Parse(XamlText);
                _table.Add(new WeakReference(contentControl), new WeakReference(r));
                return r;
            }
            catch (Exception)
            {
                return null;
            }
        }
    }
}